Skip to content

Conversation

@hsjobeki
Copy link
Contributor

@hsjobeki hsjobeki commented Nov 13, 2025

As first described here #8430

@adisbladis and me run a test where we reimplemented lib.findFirstIndex in terms of the new builtin.

  findFirstIndex =
    pred: default: list:
    let
      len = (length list);
      n = builtins.trampoline (
        idx:
        let
          found = pred (builtins.elemAt list idx);
        in
        [
          (if idx >= len then false else !found)
          (if idx >= len then -1 else if !found then idx + 1 else idx)
        ]
      ) 0;
    in
    if n == -1 then default else n;

Motivation

The idea was that findFirstIndex is exhaustive, running more iterations than necessary.
Using a trampoline it would be turned into O(n) worst case, from O(n) generally.

Observation

Running nix-instantiate pkgs.jq saved ~100 thunks

Takeway this might NOT be worth it - for the sake of exhaustive iterations in nixpkgs.
Maybe somebody has better use-case for it? I.e. the mentioned tail-recursing, or on bigger lists?


Add 👍 to pull requests you find important.

The Nix maintainer team uses a GitHub project board to schedule and track reviews.

@roberth
Copy link
Member

roberth commented Nov 13, 2025

You could polyfill trampoline by borrowing from NixOS/nixpkgs#452088.
Since those are lazy lists that don't overflow the stack, you can do:

  1. Define a sequence of trampoline iterations as a linkedList
  2. Scan that linkedList to find the "first" result in O(log n) stack space using tieredBinaryReduce

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants